package first_assignment;

import java.util.Random;

/**
 * a class for randomization and distribution group creation.
 * 
 * @author Peretz Oded, Castel Maayan, Farchy Ofir.
 */
public class DistributionCreator 
{
	private int[] moviesList;
	private double[] probabilityList;
	private int k = Consts_1st.ePB_UNINIT;
	public int a;
	public int M;
	
	/**
	 * calculates the number of different distribution groups.
	 * @param a - distribution factor (a,100-a).
	 * @param M - the size of the movie database.
	 */
	private int CalcK(int a,int M)
	{
		this.M = M;
		this.a = a;
		double log1 = java.lang.Math.log(20.0/(double)M);
		double log2 = (java.lang.Math.log((100-(double)a)/100.0));
		double tmp_k = log1/log2;
		return (int)java.lang.Math.ceil(tmp_k);
	}
	
	/**
	 * initializes the probability and size of each distribution group.
	 * @param a - distribution factor (a,100-a).
	 * @param M - the size of the movie database.
	 */
	public void InitMovieSet(int a,int M)
	{
		int newLargeSetSize;
		int smallSetSize = M;
		double largeSetPercent = 100;
		k = CalcK(a,M);		// Calc number of sets (k+1)
		moviesList = new int[k+1];
		probabilityList = new double[k+1];
		
		//tmpArr = new int[k+1];
		
		// Init List
		for (int i = k; i > 0; --i)
		{
			newLargeSetSize = (int)java.lang.Math.ceil(smallSetSize*((double)a/100.0));
			smallSetSize = smallSetSize - newLargeSetSize;
			moviesList[i] = newLargeSetSize;
			probabilityList[i] = largeSetPercent - (double)a/100.0*largeSetPercent;
			largeSetPercent = (double)a/100.0*largeSetPercent;
		}
		moviesList[0] = smallSetSize;
		probabilityList[0] = largeSetPercent;
		
		for (int j = 1; j <= k; ++j)
		{
			probabilityList[j]= probabilityList[j-1] + probabilityList[j];
			moviesList[j]= moviesList[j-1] + moviesList[j];
		}
	}
	
	/**
	 * create a random number (subject to probability constraints).
	 * to prevent erroneous usage default values are set and an initialization
	 * always occurs (if not done yet) before a random number is produced.
	 * @param M - size of movie database.
	 * @return a random number subject to probability constraints.
	 */
	public int getRandomNumber(int M)
	{
		int cellIndex = 0;
		Random generator = new Random();
		double randomNum = (generator.nextInt(M)+1)/(double)M*100; // between 1/M - 100

		if (Consts_1st.ePB_UNINIT == k) {
			InitMovieSet(Consts_1st.a, Consts_1st.M);
		}
		// Search for the probability range
		boolean found = false;
		int buttomIndex = 0;
		int topIndex = k;
		int checkIndex;
		while (!found)
		{
			checkIndex = (int)java.lang.Math.floor((buttomIndex + topIndex)/2.0);
			if (topIndex == buttomIndex + 1)
			{
				if(randomNum > probabilityList[checkIndex])
				{
					cellIndex = topIndex;
				}
				else
				{
					cellIndex = buttomIndex;
				}
				found = true;
			}
			else if (checkIndex!=0 && randomNum <= probabilityList[checkIndex] && randomNum > probabilityList[checkIndex-1])
			{
				found = true;
				cellIndex = checkIndex;
			}
			else if (checkIndex == 0 && randomNum <= probabilityList[checkIndex])
			{
				found = true;
				cellIndex = checkIndex;
			}
			else
			{
				if (randomNum > probabilityList[checkIndex])
				{
					buttomIndex = checkIndex;
				}
				else
				{
					topIndex = checkIndex;
				}
			}
		}
		
		int numOfMoviesToChooseFrom;
		int randomMovie;
		
		//tmpArr[cellIndex]++;
		
		if (cellIndex == 0)
		{
			numOfMoviesToChooseFrom = moviesList[cellIndex];
			randomMovie = generator.nextInt(numOfMoviesToChooseFrom)+1;
		}
		else
		{
			numOfMoviesToChooseFrom = moviesList[cellIndex] -  moviesList[cellIndex-1];
			randomMovie = (generator.nextInt(numOfMoviesToChooseFrom)+1);
			randomMovie = moviesList[cellIndex-1] + randomMovie;
		}
		
		return randomMovie;
	}
	
	/**
	 * returns number of members in the desired popularity class. 
	 * @param popClass - the number of the desired popularity class (0-based numbering).
	 * @return number of members in the desired popularity class or an error.
	 */
	public int getPopularityClassSize(int popClass) {
		if (Consts_1st.ePB_UNINIT == k) { return Consts_1st.ePB_UNINIT; }
		if (popClass >= k+1 || 0 > popClass) { return Consts_1st.eINVALID; }
		return moviesList[popClass] - ((0 == popClass)? 0 : moviesList[popClass-1]);
	}
	
	/**
	 * returns the probability of members in the desired popularity class. 
	 * @param popClass - the number of the desired popularity class (0-based numbering).
	 * @return probability of members in the desired popularity class or an error.
	 */
	public double getPopularityClassProbabilty(int popClass) {
		if (Consts_1st.ePB_UNINIT == k) { return (double)Consts_1st.ePB_UNINIT; }
		if (popClass >= k+1 || 0 > popClass) { return (double)Consts_1st.eINVALID; }
		return probabilityList[popClass] - ((0 == popClass)? 0 : probabilityList[popClass-1]);
	}
	
	/**
	 * returns the number of different popularity classes according to the given probability.
	 * @return number of different popularity classes.
	 */
	public int getPopularityClassNumber() {
		return k+1;
	}
}
